/*
 * Particle.java
 *
 * Created on January 30, 2007, 8:30 AM
 *
 * @author Greg Robinson gregarobi@dev.java.net
 */

package pso;
public class Particle {
    
    //Particle info
    private double[] velocity; //Current velocity
    private double[] position; //Current position
    private double[] bestPosition; //Historical best position
    private double bestFitness; //Prevents unnecessary calls to fitness()
    
    private Swarm ps; //Reference to swarm
    
    /** Creates a new instance of Particle */
    public Particle(Swarm ps) {
        this.ps = ps; //Make particle swarm-aware by reference to save IO/mem
        int nDimensions = ps.nDimensions;
        this.bestPosition = new double[nDimensions]; //Particle historical bestPosition position
        this.velocity = new double[nDimensions]; //Current velocity
        this.position = new double[nDimensions]; //Current position
        
        this.position = fillPosition(ps.spaceSize);
        this.velocity = fillPosition(ps.spaceSize);
    }
    
    private double rand(double min, double max){
        return min + Math.random() * (max - min);
    }
    
    private double[] fillPosition(double[][] limits){
        double[] position = new double[limits.length];
        for (int i = 0; i < limits.length; i++){
            position[i] = rand(limits[i][0], limits[i][1]);
        }
        return position;
    }
    
    public void updateVelocity(){
        double vi; // Scalar velocity component in each dimension
        
        for (int i = 0; i < this.velocity.length; i++){ //for each dimension
            
            // If the particle is pinned to boundary, unpin it
            if (this.position[i] == ps.spaceSize[i][0]
                    || this.position[i] == ps.spaceSize[i][1]){
                vi = 0;
            }
            // Set scalar velocity component using this formula
            // Vi = K(Vid+phi1*rand()*(Pid-Xid)+phi2*rand()*(Pgd-Xid)
            vi = ps.K * (
                    //Current velocity
                    this.velocity[i] +
                    //Cognitive component
                    ps.phi1*rand(this.bestPosition[i], this.position[i]) +
                    //Social component
                    ps.phi2*rand(ps.getGlobalBest()[i], this.position[i])
                    ); //end vi=...
            this.velocity[i] = vi; //Replace velocity component in dimension 'i'
        } //for each dimension
    } // updateVelocity()
    
    /**
     * Updates position and enforces spacial boundaries
     *
     * @return particle's historical best fitness
     */
    public double updatePosition(){
        for (int i = 0; i < this.position.length; i++){ // for each dimension
            double newPosition = this.position[i] + this.velocity[i];
            
            // If within position limits
            if (this.ps.spaceSize[i][0] <= newPosition && // Greater than min
                    this.ps.spaceSize[i][1] >= newPosition // Less than max
                    ){
                this.position[i] = newPosition;
            } else if (this.ps.spaceSize[i][0] > newPosition){ //Less than min
                this.position[i] = this.ps.spaceSize[i][0]; //Set to min
            } else if (this.ps.spaceSize[i][1] < newPosition){ //Greater than max
                this.position[i] = this.ps.spaceSize[i][1]; //Set to max
            } // If within position limits
        } // for each dimension
        return this.updateFitness();
    } // end updatePosition()
    
    /**
     * Updates this.bestPosition and this.bestFitness
     *
     * @return the particle's best fitness.
     */
    public double updateFitness(){
        double currentFitness = ps.fitness(this.position);
        if (currentFitness < this.bestFitness){
            this.bestFitness = currentFitness;
            this.bestPosition = this.position;
        } //if (currentFitness = this.bestFitness)
        return this.bestFitness;
    }
    
    public double[] getPos(){
        return this.bestPosition;
    }
    
    public void printInfo(){    
        System.out.println("vx="+velocity[0]);
        System.out.println("vy="+velocity[1]);
        System.out.println("vz="+velocity[2]);
        System.out.println("x="+this.position[0]);
        System.out.println("y="+this.position[1]);
        System.out.println("z="+this.position[2]);
        System.out.println("xbest="+this.bestPosition[0]);
        System.out.println("ybest="+this.bestPosition[1]);
        System.out.println("zbest="+this.bestPosition[2]);
        System.out.println("Fitness: "+ps.fitness(this.position));
        System.out.println();
    }
    
} // end class Particle
